#include "table.h"
#include <map>

void Table::table_foreignkey_join(Table& t1,string t1col, Table& t2,string t2col, Table& res) {
	int t1_fk_index = -1;
	int t2_mk_index = -1;
	for (int i = 0; i < t1.column_names.size(); i++) {
		if (t1.column_names[i] == t1col) {
			t1_fk_index = i;
		}
	}
	for (int i = 0; i < t2.column_names.size(); i++) {
		if (t2.column_names[i] == t2col) {
			t2_mk_index = i;
		}
	}
	if (t1_fk_index == -1 || t2_mk_index == -1) {
		cout << "[Error   ] fk join error no focus on col" << endl;
		exit(0);
	}

	res.table_name = "(" + t1.table_name + "_fj_" + t2.table_name + ")";
	res.dblocal = false;
	
	for (int i = 0; i < t1.column_names.size(); i++) {
		res.column_names.push_back(t1.column_names[i]);
	}
	for (int i = 0; i < t2.column_names.size(); i++) {
		res.column_names.push_back(t2.column_names[i]);
	}
	for (int i = 0; i < res.column_names.size(); i++) {
		res.values.push_back(vector<string>());
	}


	int t2_flag = 0;
	int t1_size = t1.values[0].size();
	int t2_size = t2.values[0].size();
	//cout << "t1 size : " << t1_size << endl;
	for (int i = 0; i < t1_size; i++) {
		
		while (t2_flag < t2_size && stoi(t1.values[t1_fk_index][i]) > stoi(t2.values[t2_mk_index][t2_flag]) ) {
			t2_flag++;
		}
		if (t2_flag >= t2_size) break;

		if (stoi(t1.values[t1_fk_index][i]) == stoi(t2.values[t2_mk_index][t2_flag])) {
			//cout << "accept" << endl;
			int col_num = 0;
			for (int j = 0; j < t1.column_names.size(); j++) {
				res.values[j].push_back(t1.values[j][i]);
			}
			col_num = t1.column_names.size();

			for (int j = 0; j < t2.column_names.size(); j++) {
				res.values[col_num].push_back(t2.values[j][t2_flag]);
				col_num++;
			}
		}
	}

}

void Table::table_print(int limit) {
	int all_row = values[0].size();
	cout << "table name : " << table_name  << " rows : " << all_row  << endl;
	for (int i = 0; i < column_names.size(); i++) {
		cout << column_names[i] << "\t" ;
	}
	cout << endl;
	
	int t_size = min( all_row,limit);
	for (int i = 0; i < t_size; i++) {
		for (int j = 0; j < column_names.size(); j++) {
			cout << values[j][i] << "\t";
		}
		cout << endl;
	}
}

void Table::input_data(PGconn* conn, string tablename, string sql,vector<string> col_names) {
	table_name = tablename;

	/*
	string sql_col = "SELECT column_name FROM information_schema.COLUMNS WHERE TABLE_NAME = '" + tablename + "'";
	PGresult* res = PQexec(conn, sql_col.c_str());
	if (PQresultStatus(res) != PGRES_TUPLES_OK) {
		cout << "[Error   ] execute : " << sql_col << endl;
		PQclear(res);
		exit(0);
	}
	int res_row = PQntuples(res);
	for (int i = 0; i < res_row; i++) {
		column_names.push_back(string(PQgetvalue(res, i, 0)));
	}
	PQclear(res);
	*/
	column_names = col_names;

	PGresult* res = PQexec(conn, sql.c_str());
	if (PQresultStatus(res) != PGRES_TUPLES_OK) {
		cout << "[Error   ] execute : " << sql << endl;
		PQclear(res);
		exit(0);
	}
	int res_row = PQntuples(res);
	int res_col = PQnfields(res);
	if (res_col != column_names.size()) {
		cout << "[Error] table colomn size != sql res" << endl;
		PQclear(res);
		exit(0);
	}
	for (int j = 0; j < res_col; j++) {
		values.push_back(vector<string>());
	}
	for (int i = 0; i < res_row; i++) {
		for (int j = 0; j < res_col; j++) {
			values[j].push_back(PQgetvalue(res, i, j));
		}
	}
	dblocal = true;
	cout << "[logging ] input : " << tablename << "  data, rows : " << values[0].size() << endl;
}

void Table::table_normal_join(Table& t1, Table& t2, Table& res) {
	
	res.table_name = "(" + t1.table_name + "_nj_" + t2.table_name + ")";
	res.dblocal = false;

	for (int i = 0; i < t1.column_names.size(); i++) {
		res.column_names.push_back(t1.column_names[i]);
	}
	for (int i = 0; i < t2.column_names.size(); i++) {
		res.column_names.push_back(t2.column_names[i]);
	}
	for (int i = 0; i < res.column_names.size(); i++) {
		res.values.push_back(vector<string>());
	}

	int t1_row = t1.values[0].size();
	int t2_row = t2.values[0].size();
	int t1_cols = t1.column_names.size();
	int t2_cols = t2.column_names.size();

	for (int i = 0; i < t1_row; i++) {
		for (int j = 0; j < t2_row; j++) {
			for (int k = 0; k < t1_cols; k++) {
				res.values[k].push_back(t1.values[k][i]);
			}
			for (int k = 0; k < t2_cols; k++) {
				res.values[k + t1_cols].push_back(t2.values[k][j]);
			}
		}
	}

}

void Table::add_tracebit() {
	string tbit_col = table_name + "_tbit";
	column_names.push_back(tbit_col);
	values.push_back(vector<string>());
	int row = values[0].size();
	int tbit_num = values.size() - 1;
	for (int i = 0; i < row; i++) {
		values[tbit_num].push_back(to_string(i));
	}
}

void Table::execute_where(Predicate& pred){
	if ( pred.left_iscol == false) return;
	if (pred.right_iscol == false) return;

	string left_colname = pred.left_value;
	string right_colname = pred.right_value;
	int left_index = -1;
	int right_index = -1;
	for (int i = 0; i < column_names.size(); i++) {
		if (column_names[i] == left_colname) {
			left_index = i;
		}
		if (column_names[i] == right_colname) {
			right_index = i;
		}
	}
	if (left_index == -1 or right_index == -1) {
		cout << "[Error   ] does not exist col " << left_colname  << " or " << right_colname << endl;
		exit(0);
	}

	if (pred.mid_op == ">") {
		for (int i = 0; i < values[0].size(); i++) {
			if (stod(values[left_index][i]) <=  stod(values[right_index][i])) {
				for (int j = 0; j < column_names.size(); j++) {
					values[j][i] = values[j].back();
					values[j].pop_back();
				}
				i--;
			}
		}
	}
	

}

void Table::orderby_Col(string orderby_col) {
	vector<vector<string>> values_temp;
	for (int j = 0; j < column_names.size(); j++) {
		values_temp.push_back(vector<string>());
	}

	int orderby_index = -1;
	for (int j = 0; j < column_names.size(); j++) {
		if (column_names[j] == orderby_col) {
			orderby_index = j;
		}
	}
	if (orderby_index == -1) {
		cout << "[Error   ] not find orderby col : " << orderby_col << endl;
	}
	map<int, vector<int>> order_map;
	for (int i = 0; i < values[0].size(); i++) {
		if (order_map.count( stoi(values[orderby_index][i]) ) == 0) {
			order_map[stoi(values[orderby_index][i])] = vector<int>();
		}
		order_map[stoi(values[orderby_index][i])].push_back(i);
	}

	for (auto& p : order_map) {
		vector<int> indexs = p.second;
		for(int index : indexs){
			for (int j = 0; j < column_names.size(); j++) {
				values_temp[j].push_back(values[j][index]);
			}
		}
	}
	values = move(values_temp);

}